package cn.juque.jdfsystem.service.impl;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.juque.common.base.PageInfo;
import cn.juque.common.constants.MessageEnum;
import cn.juque.common.constants.YesOrNoEnum;
import cn.juque.common.exception.AppException;
import cn.juque.common.utils.FastJsonUtil;
import cn.juque.jdfsystem.cache.ConfigInfoCache;
import cn.juque.jdfsystem.entity.ConfigInfo;
import cn.juque.jdfsystem.entity.RegisterInfo;
import cn.juque.jdfsystem.enums.ConfigCodeEnum;
import cn.juque.jdfsystem.enums.ConfigScopeEnum;
import cn.juque.jdfsystem.enums.JdfSystemMsgEnum;
import cn.juque.jdfsystem.service.IConfigInfoService;
import cn.juque.jdfsystem.service.ITransferService;
import cn.juque.lucenecandy.core.base.*;
import cn.juque.lucenecandy.core.constants.StrConstant;
import cn.juque.lucenecandy.core.enums.MatchTypeEnum;
import cn.juque.lucenecandy.helper.IndexHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.BooleanClause;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author JUQUE
 * @version 1.0.0
 * <ul>
 *  <li>simple-generator</li>
 *  <li>配置信息</li>
 * </ul>
 * @date 2023-01-08 14:38:52
 **/
@Slf4j
@Service("configInfoService")
public class ConfigInfoServiceImpl implements IConfigInfoService {

    @Resource
    private IndexHelper indexHelper;

    @Resource
    private ConfigInfoCache configInfoCache;

    @Resource
    private ITransferService transferService;

    /**
     * 分页查询
     *
     * @param pageInfo   pageInfo
     * @param configInfo configInfo
     * @return DataGrid
     */
    @Override
    public List<ConfigInfo> listInfoWithPage(PageInfo pageInfo, ConfigInfo configInfo) {
        QueryWrapperBuilder<ConfigInfo> builder = this.buildQuery(configInfo);
        builder.pageInfo(pageInfo);
        return this.indexHelper.searchByPage(builder.build());
    }

    /**
     * 查询
     *
     * @param configInfo configInfo
     * @return List
     */
    @Override
    public List<ConfigInfo> listInfoWithoutPage(ConfigInfo configInfo) {
        QueryWrapperBuilder<ConfigInfo> builder = this.buildQuery(configInfo);
        return this.indexHelper.search(builder.build());
    }

    /**
     * 详情
     *
     * @param id 主键
     * @return ConfigInfo
     */
    @Override
    public ConfigInfo detail(String id) {
        IdQueryWrapperBuilder<ConfigInfo> idQueryWrapperBuilder = new IdQueryWrapperBuilder<>(ConfigInfo.class, id);
        return this.indexHelper.searchById(idQueryWrapperBuilder.build());
    }

    /**
     * 保存
     *
     * @param configInfo configInfo
     */
    @Override
    public void saveInfo(ConfigInfo configInfo) {
        CountQueryWrapperBuilder<ConfigInfo> countQueryWrapperBuilder = new CountQueryWrapperBuilder<>(ConfigInfo.class);
        countQueryWrapperBuilder.matchStr(ConfigInfo::getConfigCode, configInfo.getConfigCode(), MatchTypeEnum.TERM, BooleanClause.Occur.MUST);
        long count = this.indexHelper.count(countQueryWrapperBuilder.build());
        if (count > 0) {
            throw new AppException(JdfSystemMsgEnum.CONFIG_CODE_EXISTS);
        }
        this.indexHelper.addDocument(configInfo);
        this.configInfoCache.refresh();
        this.checkTransfer(configInfo);
        this.checkLogger(configInfo);
    }

    /**
     * 更新
     *
     * @param configInfo configInfo
     */
    @Override
    public void updateInfo(ConfigInfo configInfo) {
        IdQueryWrapperBuilder<ConfigInfo> idQueryWrapperBuilder =
                new IdQueryWrapperBuilder<>(ConfigInfo.class, configInfo.getId());
        ConfigInfo source = this.indexHelper.searchById(idQueryWrapperBuilder.build());
        this.checkRegisterAesKey(configInfo);
        configInfo = this.indexHelper.updateDocumentById(configInfo);
        this.configInfoCache.refresh();
        this.updateRegister(source, configInfo);
        this.checkTransfer(configInfo);
        this.checkLogger(configInfo);
    }

    /**
     * 删除
     *
     * @param ids 主键列表
     */
    @Override
    public void deleteInfo(List<String> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        IdsQueryWrapperBuilder<ConfigInfo> idsQueryWrapperBuilder = new IdsQueryWrapperBuilder<>(ConfigInfo.class, ids);
        List<ConfigInfo> list = this.indexHelper.searchByIds(idsQueryWrapperBuilder.build());
        list = list.stream().filter(f -> ConfigScopeEnum.SYSTEM.getCode().equals(f.getScope())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(list)) {
            throw new AppException(MessageEnum.DELETE_FAIL.getCode(), "系统级别的配置不允许删除");
        }
        DeleteByIdsWrapperBuilder<ConfigInfo> deleteByIdsWrapperBuilder = new DeleteByIdsWrapperBuilder<>(ConfigInfo.class, ids);
        this.indexHelper.deleteByIds(deleteByIdsWrapperBuilder.build());
        this.configInfoCache.refresh();
    }

    /**
     * 初始化
     */
    @Override
    public void init() {
        QueryWrapperBuilder<ConfigInfo> queryWrapperBuilder = new QueryWrapperBuilder<>(ConfigInfo.class);
        queryWrapperBuilder.matchInt(ConfigInfo::getScope, ConfigScopeEnum.SYSTEM.getCode(), BooleanClause.Occur.MUST);
        List<ConfigInfo> list = this.indexHelper.search(queryWrapperBuilder.build());
        Set<String> codeSet = list.stream().map(ConfigInfo::getConfigCode).collect(Collectors.toSet());
        String initStr = ResourceUtil.readUtf8Str("init/config_info.json");
        List<ConfigInfo> initList = FastJsonUtil.parseArray(initStr, ConfigInfo.class);
        List<ConfigInfo> docList = initList.stream()
                .filter(f -> {
                    if (!codeSet.contains(f.getConfigCode())) {
                        f.setAttachInfo(CollUtil.newHashSet(StrConstant.NOT_SYNC));
                        return true;
                    }
                    return false;
                }).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(docList)) {
            this.indexHelper.addDocuments(docList);
        }
        log.info("系统配置初始化完成");
    }

    private QueryWrapperBuilder<ConfigInfo> buildQuery(ConfigInfo configInfo) {
        QueryWrapperBuilder<ConfigInfo> builder = new QueryWrapperBuilder<>(ConfigInfo.class);
        if (CharSequenceUtil.isNotEmpty(configInfo.getConfigName())) {
            builder.matchStr(ConfigInfo::getConfigName, configInfo.getConfigName(), MatchTypeEnum.ALL_LIKE, BooleanClause.Occur.MUST);
        }
        if (CharSequenceUtil.isNotEmpty(configInfo.getConfigCode())) {
            builder.matchStr(ConfigInfo::getConfigName, configInfo.getConfigCode(), MatchTypeEnum.TERM, BooleanClause.Occur.MUST);
        }
        return builder;
    }

    /**
     * 检查是否开始迁移
     *
     * @param configInfo 配置信息
     */
    private void checkTransfer(ConfigInfo configInfo) {
        Set<String> set = CollUtil.newHashSet(ConfigCodeEnum.TRANSFER_START.getCode(),
                ConfigCodeEnum.TRANSFER_DRIVER.getCode(), ConfigCodeEnum.TRANSFER_URL.getCode(),
                ConfigCodeEnum.TRANSFER_USERNAME.getCode(), ConfigCodeEnum.TRANSFER_PASSWORD.getCode());
        if (!set.contains(configInfo.getConfigCode())) {
            return;
        }
        if(YesOrNoEnum.YES.getCode().equals(configInfo.getSyncFlag())) {
            throw new AppException(MessageEnum.PARAM_ERROR.getCode(), "迁移数据的配置不允许节点间同步");
        }
        String code = this.configInfoCache.getValue(ConfigCodeEnum.TRANSFER_START.getCode());
        if (CharSequenceUtil.isEmpty(code)) {
            return;
        }
        // 是否开启迁移
        if (YesOrNoEnum.YES.getCode().equals(Integer.parseInt(code))) {
            this.transferService.transferData();
        }
    }

    private void checkLogger(ConfigInfo configInfo) {
        if(!ConfigCodeEnum.LOGGER.getCode().equals(configInfo.getConfigCode())) {
            return;
        }
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        Logger logger = loggerContext.getLogger("cn.juque.jdfsystem");
        Level level = Level.toLevel(configInfo.getConfigValue());
        logger.setLevel(level);
        logger = loggerContext.getLogger("cn.juque.lucenecandy");
        logger.setLevel(level);
    }

    private void updateRegister(ConfigInfo old, ConfigInfo configInfo) {
        QueryWrapperBuilder<RegisterInfo> queryWrapperBuilder = new QueryWrapperBuilder<>(RegisterInfo.class);
        queryWrapperBuilder
                .matchStr(RegisterInfo::getIp, old.getConfigValue(), MatchTypeEnum.TERM, BooleanClause.Occur.SHOULD)
                .matchStr(RegisterInfo::getWorkspaceId, old.getConfigValue(), MatchTypeEnum.TERM, BooleanClause.Occur.SHOULD)
                .matchStr(RegisterInfo::getNodeName, old.getConfigValue(), MatchTypeEnum.TERM, BooleanClause.Occur.SHOULD);
        List<RegisterInfo> list = this.indexHelper.search(queryWrapperBuilder.build());
        if (CollUtil.isEmpty(list)) {
            throw new AppException(MessageEnum.SYSTEM_ERROR.getCode(), "服务注册信息丢失");
        }
        if (!ConfigCodeEnum.BIND_IP.getCode().equals(configInfo.getConfigCode())
                && !ConfigCodeEnum.NODE_NAME.getCode().equals(configInfo.getConfigCode())
                && !ConfigCodeEnum.WORKSPACE_ID.getCode().equals(configInfo.getConfigCode())) {
            return;
        }
        RegisterInfo registerInfo = list.get(0);
        registerInfo.setAttachInfo(CollUtil.newHashSet(StrConstant.NOT_SYNC));
        if (ConfigCodeEnum.BIND_IP.getCode().equals(configInfo.getConfigCode())) {
            registerInfo.setIp(configInfo.getConfigValue());
            this.indexHelper.updateDocumentById(registerInfo);
        }
        if (ConfigCodeEnum.WORKSPACE_ID.getCode().equals(configInfo.getConfigCode())) {
            registerInfo.setWorkspaceId(configInfo.getConfigValue());
            this.indexHelper.updateDocumentById(registerInfo);
        }
        if (ConfigCodeEnum.NODE_NAME.getCode().equals(configInfo.getConfigCode())) {
            registerInfo.setNodeName(configInfo.getConfigValue());
            this.indexHelper.updateDocumentById(registerInfo);
        }
        log.info("完成注册信息更新, ip:{}, 工作空间:{}, 节点名称:{}",
                registerInfo.getIp(), registerInfo.getWorkspaceId(), registerInfo.getNodeName());
    }

    private void checkRegisterAesKey(ConfigInfo configInfo) {
        if(!ConfigCodeEnum.REGISTER_AES_KEY.getCode().equals(configInfo.getConfigCode())) {
            return;
        }
        if(YesOrNoEnum.YES.getCode().equals(configInfo.getSyncFlag())) {
            throw new AppException(MessageEnum.PARAM_ERROR.getCode(), "秘钥不允许节点间同步");
        }
        if(CharSequenceUtil.isBlank(configInfo.getConfigValue())) {
            String key = Base64.encode(SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded());
            configInfo.setConfigValue(key);
        }
    }
}
